﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Web;
using System.Threading;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using System.Data.Common;
using System.Data;
using System.Collections;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Husb.Util.Enum;
using System.IO;

namespace Husb.Framework.DataAccess
{
    public abstract class DataAccessBase<TEntity> 
        where TEntity : EntityBase, new()
    {
        //private Database db;
        public Database Database
        {
            get
            {
                Database d = null;
                #region 根据应用程序的种类不同处理数据库类
                // 如果是asp.net程序
                if (HttpContext.Current != null)
                {
                    d = HttpContext.Current.Application["MainDatabase"] as Database;
                    if (d == null)
                    {
                        d = DatabaseFactory.CreateDatabase();
                        HttpContext.Current.Application["MainDatabase"] = d;
                    }
                }
                else
                {
                    // 如果是WPF程序
                    if (System.Windows.Application.Current != null)
                    {

                        d = System.Windows.Application.Current.Properties["MainDatabase"] as Database;
                        if (d == null)
                        {
                            d = DatabaseFactory.CreateDatabase();
                            System.Windows.Application.Current.Properties["MainDatabase"] = d;
                        }
                    }
                    else
                    {
                        // 如果是Winform程序
                        LocalDataStoreSlot slot = Thread.GetNamedDataSlot("MainDatabase");
                        d = Thread.GetData(slot) as Database;
                        if (d == null)
                        {
                            d = DatabaseFactory.CreateDatabase();
                            Thread.SetData(slot, d);
                        }
                    }
                }
                #endregion
                return d;
            }
        }

        private DbCommand selectCommand = null;
        private DbCommand insertCommand = null;
        private DbCommand updateCommand = null;
        private DbCommand deleteCommand = null;

        private CommandType commandType;

        public CommandType CommandType
        {
            get { return commandType; }
            set { commandType = value; }
        }

        #region 
        public string MasterObjectId = "";
        public string DetailObjectId = "";
        #endregion

        #region Common StoredProcedure Name List

        /// <summary>
        /// 更新实体类的存储过程名
        /// </summary>
        public string UpdateStatement;
        /// <summary>
        /// 更新从表的存储过程名
        /// </summary>
        public string UpdateDetailStatement;
        /// <summary>
        /// 更新父表的存储过程名
        /// </summary>
        public string UpdateMasterStatement;
        /// <summary>
        /// 根据识别号删除实体类的存储过程名
        /// </summary>
        public string DeleteByIdStatement;
        /// <summary>
        /// 根据一个不完整的对象删除实体类的存储过程名
        /// </summary>
        public string DeleteByObjectStatement;
        /// <summary>
        /// 根据识别号删除实体类的存储过程名
        /// </summary>
        public string DeleteDetailByIdStatement;

        /// <summary>
        /// 根据识别号获取实体类的存储过程名
        /// </summary>
        public string GetByIdStatement;
        /// <summary>
        /// 根据完整名称获取一个实体类的存储过程名
        /// </summary>
        public string GetByNameStatement;
        /// <summary>
        /// 根据完整名称或不完整获取一个或多个实体类的存储过程名
        /// </summary>
        public string FindByNameStatement;
        /// <summary>
        /// 获取所有实体类的存储过程名
        /// </summary>
        public string FindAllStatement;
        /// <summary>
        /// 根据Owner获取所有实体类的存储过程名
        /// </summary>
        public string GetAllByOwnerStatement;
        /// <summary>
        /// 根据动态过滤条件获取实体类的存储过程名
        /// </summary>
        public string FindStatement;
        /// <summary>
        /// 根据主对象识别号获取当前对象集合的存储过程名
        /// </summary>
        public string GetByMasterIdStatement;

        /// <summary>
        /// 根据当前对象的识别号获取从对象集合的存储过程名
        /// </summary>
        public string GetDetailObjectStatement;
        /// <summary>
        /// 根据识别号获取主对象的存储过程名
        /// </summary>
        public string GetMasterObjectStatement;
        /// <summary>
        /// 获取记录数的存储过程名
        /// </summary>
        public string GetRowCountStatement;
        /// <summary>
        /// 获取分页记录的存储过程名
        /// </summary>
        public string GetPagedStatement;

        /// <summary>
        /// 根据识别号获取一个值的存储过程名
        /// </summary>
        public string GetValueByIdStatement;

        /// <summary>
        /// 保存一个对象的存储过程名
        /// </summary>
        public string InsertStatement;
        /// <summary>
        /// 保存的同时可以更新一个对象的存储过程名
        /// </summary>
        public string CreateUpdateStatement;

        /// <summary>
        /// 保存从对象的存储过程名
        /// </summary>
        public string CreateDetailStatement;
        /// <summary>
        /// 保存主对象的存储过程名
        /// </summary>
        public string CreateMasterStatement;

        #endregion

        #region  Virtual Methods
        protected abstract void LoadProperty(TEntity entity, IDataReader dr); // Action<TEntity, IDataReader>
        protected abstract void       PopulateParamters(TEntity entity, Database db, DbCommand cmd);
        protected virtual void PopulateUpdateParameters(TEntity entity, Database db, DbCommand cmd)
        {
            PopulateParamters(entity, db, cmd);
        }

        protected virtual void PopulateInsertParameters(TEntity entity, Database db, DbCommand cmd)
        {
            PopulateParamters(entity, db, cmd);
        }

        protected virtual void PopulateDeleteParameters(Database db, DbCommand cmd, Guid id)
        {
            PopulateGuidIdParameters(db, cmd, id);
        }

        //protected abstract void PopulateInsertParameters(Microsoft.Practices.EnterpriseLibrary.Data.Database db, DbCommand cmd);

        #endregion

        #region PrepareCommand

        private DbCommand GetSelectCommand(string commandText, CommandType cmdType)
        {
            if (selectCommand == null)
            {
                if (cmdType == CommandType.StoredProcedure)
                {
                    selectCommand = Database.GetStoredProcCommand(commandText);
                }
                else
                {
                    selectCommand = Database.GetSqlStringCommand(commandText);
                }
            }
            else
            {
                selectCommand.Parameters.Clear();
                selectCommand.CommandText = commandText;
            }

            return selectCommand;
        }

        private void AddReturnValueParameter(Action<int, string> handleReturnValue)
        {
            if (handleReturnValue != null)
            {
                Database.AddParameter(selectCommand, "ReturnValue", DbType.Int32, ParameterDirection.ReturnValue, String.Empty, DataRowVersion.Default, null);
            }
        }

        protected DbCommand PrepareCommand(string commandText, IEnumerable<DatabaseParameter> parameters, Action<int, string> handleReturnValue, CommandType cmdType)
        {
            selectCommand = GetSelectCommand(commandText, cmdType);

            if (parameters != null)
            {
                DataAccessHelper.PopulateParameters(parameters, Database, selectCommand);
            }

            AddReturnValueParameter(handleReturnValue);

            return selectCommand;
        }

        protected DbCommand PrepareCommand(string commandText, TEntity entity, Action<TEntity, Microsoft.Practices.EnterpriseLibrary.Data.Database, DbCommand> populateParamters, Action<int, string> handleReturnValue, CommandType cmdType)
        {
            selectCommand = GetSelectCommand(commandText, cmdType);
            
            if (populateParamters != null)
            {
                populateParamters(entity, Database, selectCommand);
            }

            AddReturnValueParameter(handleReturnValue);

            return selectCommand;
        }

        protected DbCommand PrepareCommand<T>(string commandText, Action<Microsoft.Practices.EnterpriseLibrary.Data.Database, DbCommand, T> populateParamters, T t, Action<int, string> handleReturnValue, CommandType cmdType)
        {
            selectCommand = GetSelectCommand(commandText, cmdType);

            if (populateParamters != null)
            {
                populateParamters(Database, selectCommand, t);
            }

            AddReturnValueParameter(handleReturnValue);

            return selectCommand;
        }

        protected int HandleReturnValue(Action<int, string> handleReturnValue, DbCommand cmd)
        {
            int returnValue = 0;
            if (handleReturnValue != null)
            {
                returnValue = DataAccessHelper.GetReturnValue(Database, cmd);
            }
            if (handleReturnValue != null)
            {
                handleReturnValue(returnValue, "");
            }

            return returnValue;
        }

        private T GetFromCache<T>(string cacheKey, string commandText, out string key, out ICacheManager cacheManager) where T : class
        {
            key = commandText;
            if (!string.IsNullOrEmpty(cacheKey))
            {
                key = cacheKey;
            }
            cacheManager = CacheFactory.GetCacheManager();
            return cacheManager.GetData(key) as T;
        }


        #endregion

        #region 基础方法

        protected IList<TEntity> Fetch(DbCommand cmd, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, out int returnValue, System.Collections.Specialized.IOrderedDictionary outputParameters)
        {
            IList<TEntity> entities = null;
            returnValue = 0;

            #region Cache
            ICacheManager cacheManager = null;
            string key = null;
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                cacheManager = CacheFactory.GetCacheManager();
                entities = cacheManager.GetData(key) as IList<TEntity>;
                //entities = GetFromCache<IList<TEntity>>(cacheKey, commandText, out key, out cacheManager);

                if (entities != null)
                {
                    return entities;
                }
            }
            #endregion

            #region Execute
            try
            {
                // DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, isStoredProc);

                entities = new List<TEntity>();
                using (IDataReader dr = Database.ExecuteReader(cmd))
                {
                    while (dr.Read())
                    {
                        TEntity entity = new TEntity();
                        loadEntityInfo(entity, dr);
                        entities.Add(entity);
                    }
                }

                returnValue = HandleReturnValue(handleReturnValue, cmd);

                if (outputParameters != null && outputParameters.Count > 0)
                {
                    foreach (DictionaryEntry entry in outputParameters)
                    {
                        outputParameters[entry.Key] = Database.GetParameterValue(cmd, entry.Key.ToString());
                    }
                }
            }
            catch
            {
                throw;
            }
            #endregion

            #region Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessHelper.InsertCache(key, entities, cacheManager);
            }
            #endregion

            return entities;


        }

        protected IList<TEntity> Fetch(string commandText, IEnumerable<DatabaseParameter> parameters, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType, out int returnValue, System.Collections.Specialized.IOrderedDictionary outputParameters)
        {
            DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);
            IList<TEntity> entities = Fetch(cmd, loadEntityInfo, handleReturnValue, enableCache, cacheKey, out  returnValue, outputParameters);
            return entities;
        }

        protected IList<TEntity> Fetch(string commandText, TEntity entity, Action<TEntity, Microsoft.Practices.EnterpriseLibrary.Data.Database, DbCommand> populateParamters, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType, out int returnValue, System.Collections.Specialized.IOrderedDictionary outputParameters)
        {
            DbCommand cmd = PrepareCommand(commandText, entity, populateParamters, handleReturnValue, cmdType);
            IList<TEntity> entities = Fetch(cmd, loadEntityInfo, handleReturnValue, enableCache, cacheKey, out  returnValue, outputParameters);
            return entities;
        }

        //protected IList<TEntity> Fetch(string commandText, IEnumerable<DatabaseParameter> parameters, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, bool isStoredProc, out int returnValue, System.Collections.Specialized.IOrderedDictionary outputParameters)
        //{
        //    IList<TEntity> entities = null;
        //    returnValue = 0;

        //    #region Cache
        //    ICacheManager cacheManager = null;
        //    string key = null;
        //    if (enableCache)
        //    {
        //        if (!string.IsNullOrEmpty(cacheKey))
        //        {
        //            key = cacheKey;
        //        }
        //        cacheManager = CacheFactory.GetCacheManager();
        //        entities = cacheManager.GetData(key) as IList<TEntity>;
        //        //entities = GetFromCache<IList<TEntity>>(cacheKey, commandText, out key, out cacheManager);

        //        if (entities != null)
        //        {
        //            return entities;
        //        }
        //    }
        //    #endregion

        //    #region Execute
        //    try
        //    {
        //        DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, isStoredProc);
                
        //        entities = new List<TEntity>();
        //        using (IDataReader dr = Database.ExecuteReader(cmd))
        //        {
        //            while (dr.Read())
        //            {
        //                TEntity entity = new TEntity();
        //                loadEntityInfo(entity, dr);
        //                entities.Add(entity);
        //            }
        //        }

        //        returnValue = HandleReturnValue(handleReturnValue, cmd);

        //        if (outputParameters != null && outputParameters.Count > 0)
        //        {
        //            foreach (DictionaryEntry entry in outputParameters)
        //            {
        //                outputParameters[entry.Key] = Database.GetParameterValue(cmd, entry.Key.ToString());
        //            }
        //        }
        //    }
        //    catch
        //    {
        //        throw;
        //    }
        //    #endregion

        //    #region Cache
        //    if (enableCache && cacheManager != null)
        //    {
        //        DataAccessHelper.InsertCache(key, entities, cacheManager);
        //    }
        //    #endregion

        //    return entities;
        //}

        protected IList<TEntity> Fetch(string commandText, IEnumerable<DatabaseParameter> parameters, Action<TEntity, IDataReader> loadEntityInfo, bool enableCache, string cacheKey, CommandType cmdType)
        {
            IList<TEntity> entities = null;

            #region Cache
            ICacheManager cacheManager = null;
            string key = commandText;
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                cacheManager = CacheFactory.GetCacheManager();
                entities = cacheManager.GetData(key) as IList<TEntity>;

                if (entities != null)
                {
                    return entities;
                }
            }
            #endregion

            #region Execute
            try
            {
                DbCommand cmd = PrepareCommand(commandText, parameters, null, cmdType);
                entities = new List<TEntity>();
                using (IDataReader dr = Database.ExecuteReader(cmd))
                {
                    while (dr.Read())
                    {
                        TEntity domainObj = new TEntity();
                        loadEntityInfo(domainObj, dr);
                        entities.Add(domainObj);
                    }
                }
            }
            catch
            {
                throw;
            }
            #endregion

            #region Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessHelper.InsertCache(key, entities, cacheManager);
            }
            #endregion

            return entities;
        }

        protected TEntity FetchEntity(DbCommand cmd, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey)
        {
            TEntity entity = null;

            #region Cache
            ICacheManager cacheManager = null;
            string key = cmd.CommandText;
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                cacheManager = CacheFactory.GetCacheManager();
                entity = cacheManager.GetData(key) as TEntity;
                if (entity != null)
                {
                    return entity;
                }
            }
            #endregion

            try
            {
                //DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);
                using (IDataReader dr = Database.ExecuteReader(cmd))
                {
                    if (dr.Read())
                    {
                        entity = new TEntity();
                        loadEntityInfo(entity, dr);
                    }
                }

                int returnValue = HandleReturnValue(handleReturnValue, cmd);
            }
            catch
            {
                throw;
            }

            #region Cache
            if (enableCache && cacheManager != null)
            {
                DataAccessHelper.InsertCache(key, entity, true, 0, 0, 20, cacheManager);
            }
            #endregion

            return entity;
        }

        protected TEntity FetchEntity(string commandText, IEnumerable<DatabaseParameter> parameters, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType)
        {
            DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);
            TEntity entity = FetchEntity(cmd, loadEntityInfo, handleReturnValue, enableCache, cacheKey);
            return entity;
        }

        protected TEntity FetchEntity(string commandText, TEntity entity, Action<TEntity, Microsoft.Practices.EnterpriseLibrary.Data.Database, DbCommand> populateParamters, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType)
        {
            DbCommand cmd = PrepareCommand(commandText, entity, populateParamters, handleReturnValue, cmdType);
            TEntity t = FetchEntity(cmd, loadEntityInfo, handleReturnValue, enableCache, cacheKey);
            return t;
        }

        protected TEntity FetchEntity<T>(string commandText, Action<Microsoft.Practices.EnterpriseLibrary.Data.Database, DbCommand, T> populateParamters, T t, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType)
        {
            DbCommand cmd = PrepareCommand<T>(commandText, populateParamters, t, handleReturnValue, cmdType);
            TEntity entity = FetchEntity(cmd, loadEntityInfo, handleReturnValue, enableCache, cacheKey);
            return entity;
        }

        //protected TEntity FetchEntity(string commandText, IEnumerable<DatabaseParameter> parameters, Action<TEntity, IDataReader> loadEntityInfo, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType)
        //{
        //    TEntity entity = null;

        //    #region Cache
        //    ICacheManager cacheManager = null;
        //    string key = commandText;
        //    if (enableCache)
        //    {
        //        if (!string.IsNullOrEmpty(cacheKey))
        //        {
        //            key = cacheKey;
        //        }
        //        cacheManager = CacheFactory.GetCacheManager();
        //        entity = cacheManager.GetData(key) as TEntity;
        //        if (entity != null)
        //        {
        //            return entity;
        //        }
        //    }
        //    #endregion

        //    try
        //    {
        //        DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);
        //        using (IDataReader dr = Database.ExecuteReader(cmd))
        //        {
        //            if (dr.Read())
        //            {
        //                entity = new TEntity();
        //                loadEntityInfo(entity, dr);
        //            }
        //        }

        //        int returnValue = HandleReturnValue(handleReturnValue, cmd);
        //    }
        //    catch
        //    {
        //        throw;
        //    }

        //    #region Cache
        //    if (enableCache && cacheManager != null)
        //    {
        //        DataAccessHelper.InsertCache(key, entity, true, 0, 0, 20, cacheManager);
        //    }
        //    #endregion

        //    return entity;
        //}

        protected TEntity FetchEntity(string commandText, IEnumerable<DatabaseParameter> parameters, Action<TEntity, IDataReader> loadEntityInfo, bool enableCache, string cacheKey, CommandType cmdType)
        {
            return FetchEntity(commandText, parameters, loadEntityInfo, null, enableCache, cacheKey, cmdType);
        }

        protected object GetValue(string commandText, IEnumerable<DatabaseParameter> parameters, Action<int, string> handleReturnValue, bool enableCache, string cacheKey, CommandType cmdType)
        {
            object o = null;

            #region Cache
            //this.EnableCache = enableCache;
            ICacheManager cacheManager = null;
            string key = commandText;
            if (enableCache)
            {
                if (!string.IsNullOrEmpty(cacheKey))
                {
                    key = cacheKey;
                }
                cacheManager = CacheFactory.GetCacheManager();
                o = cacheManager.GetData(key);
                if (o != null)
                {
                    return o;
                }
            }
            #endregion

            DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);
            try
            {
                o = Database.ExecuteScalar(cmd);
                HandleReturnValue(handleReturnValue, cmd);
            }
            catch (Exception ex)
            {
                if (ExceptionPolicy.HandleException(ex, ExceptionPolicyType.DataAccessPolicy.ToString("G"))) throw;
            }
            //catch (DataException ex)
            //{
            //    //if (ExceptionPolicy.HandleException(ex, SR.ExceptionWrapPolicy)) throw;
            //}

            if (enableCache && cacheManager != null)
            {
                DataAccessHelper.InsertCache(key, o, cacheManager);
            }
            return o == DBNull.Value ? null : o;
        }

        protected int ExecuteNonQuery(TEntity entity, string commandText, Action<TEntity, Database, DbCommand> populateParamters, Action<int, string> handleReturnValue, bool removeCache, string[] cacheKeys, CommandType cmdType)
        {
            int returnValue = -1;
            try
            {
                DbCommand cmd = PrepareCommand(commandText,entity, populateParamters, handleReturnValue, cmdType);
               
                returnValue = Database.ExecuteNonQuery(cmd);
                if (handleReturnValue != null)
                {
                    returnValue = HandleReturnValue(handleReturnValue, cmd);
                }
            }
            catch
            {
                throw;
            }

            // 此处用委托更为恰当，可以Remove多个缓存项
            // 或者干脆宁可错杀一千，不可放过一个:)
            if (removeCache && cacheKeys != null && cacheKeys.Length > 0)
            {
                DataAccessHelper.UpdateCacheDependency(cacheKeys);
            }

            return returnValue;
        }

        protected int ExecuteNonQuery(TEntity entity, string commandText, IEnumerable<DatabaseParameter> parameters, Action<int, string> handleReturnValue, bool removeCache, string[] cacheKeys, CommandType cmdType)
        {
            int returnValue = -1;
            try
            {
                DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);

                returnValue = Database.ExecuteNonQuery(cmd);
                if (handleReturnValue != null)
                {
                    returnValue = HandleReturnValue(handleReturnValue, cmd);
                }
            }
            catch
            {
                throw;
            }

            // 此处用委托更为恰当，可以Remove多个缓存项
            // 或者干脆宁可错杀一千，不可放过一个:)
            if (removeCache && cacheKeys != null && cacheKeys.Length > 0)
            {
                DataAccessHelper.UpdateCacheDependency(cacheKeys);
            }

            return returnValue;
        }

        protected int ExecuteNonQuery<T>(string commandText, Action<Database, DbCommand, T> populateParamters, T t, Action<int, string> handleReturnValue, bool removeCache, string[] cacheKeys, CommandType cmdType)
        {
            int returnValue = -1;
            try
            {
                DbCommand cmd = PrepareCommand(commandText, populateParamters, t, handleReturnValue, cmdType);
                returnValue = Database.ExecuteNonQuery(cmd);
                if (handleReturnValue != null)
                {
                    returnValue = HandleReturnValue(handleReturnValue, cmd);
                }
            }
            catch
            {
                throw;
            }

            // 此处用委托更为恰当，可以Remove多个缓存项
            // 或者干脆宁可错杀一千，不可放过一个:)
            if (removeCache && cacheKeys != null && cacheKeys.Length > 0)
            {
                DataAccessHelper.UpdateCacheDependency(cacheKeys);
            }

            return returnValue;
        }

        protected byte[] GetBytes(string commandText, int columnIndex, IEnumerable<DatabaseParameter> parameters, Action<int, string> handleReturnValue, CommandType cmdType)
        {
            DbCommand cmd = PrepareCommand(commandText, parameters, handleReturnValue, cmdType);

            byte[] bf = null;

            using (IDataReader dr = Database.ExecuteReader(cmd))
            {
                if (dr.Read() && (!dr.IsDBNull(columnIndex)))
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        const int bufferSize = 4096;
                        byte[] buffer = new byte[bufferSize];
                        int byteCountRead;
                        long currentIndex = 0;

                        while ((byteCountRead = (int)dr.GetBytes(columnIndex, currentIndex, buffer, 0, bufferSize)) != 0)
                        {
                            ms.Write(buffer, 0, byteCountRead);
                            currentIndex += byteCountRead;
                        }

                        bf = ms.ToArray();
                        //bf = ms.GetBuffer();
                    }
                }
            }
            if (handleReturnValue != null)
            {
                HandleReturnValue(handleReturnValue, cmd);
            }

            return bf;
        }

        protected byte[] GetBytes(string commandText, int columnIndex, IEnumerable<DatabaseParameter> parameters)
        {
            return GetBytes(commandText, columnIndex, parameters, null, this.CommandType);
        }

        #endregion

        #region Find

        public virtual IList<TEntity> FindAll(bool enableCache)
        {
            if (string.IsNullOrEmpty(FindAllStatement))
            {
                throw new Exception("FindAllStatement不能为空.");
            }
            string  key = null;
            if(enableCache)
            {
                key = FindAllStatement;
            }
            IList<TEntity> entities = Fetch(FindAllStatement, null, new Action<TEntity, IDataReader>(LoadProperty), enableCache, key, this.CommandType);

            return entities;
        }

        public virtual IList<TEntity> Find(string where, string orderBy, bool enableCache)//
        {
            if (string.IsNullOrEmpty(FindStatement))
            {
                throw new Exception("FindStatement不能为空.");
            }

            IEnumerable<DatabaseParameter> parameters = GetSearchParameters(where, orderBy);
            string key = null;
            if (enableCache)
            {
                key = FindStatement + DataAccessHelper.GetKeyStringByParamters(parameters);
            }

            IList<TEntity> entities = Fetch(FindStatement,parameters,new Action<TEntity, IDataReader>(LoadProperty), enableCache, key, this.CommandType);

            return entities;
        }

        public virtual TEntity GetById(Guid id, bool enableCache)
        {
            if (string.IsNullOrEmpty(GetByIdStatement))
            {
                throw new Exception("GetByIdStatement不能为空.");
            }
            string key = null;
            if (enableCache)
            {
                key = FindAllStatement;
            }

            //List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            //parameters.Add(new DatabaseParameter("Id", DbType.Guid, id));
            TEntity entity = FetchEntity<Guid>(GetByIdStatement, new Action<Database, DbCommand, Guid>(PopulateGuidIdParameters), id, new Action<TEntity, IDataReader>(LoadProperty), null, enableCache, key, this.CommandType);
            return entity;
        }

        public virtual TEntity GetByIntId(int id, bool enableCache)
        {
            if (string.IsNullOrEmpty(GetByIdStatement))
            {
                throw new Exception("GetByIdStatement不能为空.");
            }
            string key = null;
            if (enableCache)
            {
                key = FindAllStatement;
            }
            
            TEntity entity = FetchEntity<Int32>(GetByIdStatement, new Action<Database, DbCommand, Int32>(PopulateInt32IdParameters), id, new Action<TEntity, IDataReader>(LoadProperty), null, enableCache, key, this.CommandType);
            return entity;
        }

        public virtual TEntity GetByStringId(string id, bool enableCache)
        {
            if (string.IsNullOrEmpty(GetByIdStatement))
            {
                throw new Exception("GetByIdStatement不能为空.");
            }
            string key = null;
            if (enableCache)
            {
                key = FindAllStatement;
            }

            TEntity entity = FetchEntity<String>(GetByIdStatement, new Action<Database, DbCommand, String>(PopulateStringIdParameters), id, new Action<TEntity, IDataReader>(LoadProperty), null, enableCache, key, this.CommandType);
            return entity;
        }

        public virtual IList<TEntity> GetByMasterId(Guid masterId)
        {
            if (string.IsNullOrEmpty(GetByMasterIdStatement))
            {
                throw new Exception("GetByMasterIdStatement不能为空.");
            }
            if (string.IsNullOrEmpty(MasterObjectId))
            {
                throw new Exception("MasterObjectId不能为空.");
            }
            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            parameters.Add(new DatabaseParameter(MasterObjectId, DbType.Guid, masterId));

            //List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            //parameters.Add(new DatabaseParameter("Id", DbType.Guid, id));
            IList<TEntity> entities = Fetch(GetByMasterIdStatement, parameters, new Action<TEntity, IDataReader>(LoadProperty), false, null, this.CommandType);
            return entities;
        }

        #endregion

        #region Paging

        public int GetCount(string where, bool hideDeleted)
        {
            if (string.IsNullOrEmpty(GetRowCountStatement))
            {
                throw new Exception("获取记录数的存储过程或者SQL语句：GetRowCountStatement必须要在子类中赋值!");
            }
            int recordCount = 0;
            if (hideDeleted)
            {
                where = DataAccessHelper.AppendDeletedWhereClause(where);
            }
            object o = GetValue(GetRowCountStatement, GetSearchParameters(where, null), null, false, null, this.commandType);//GetRowCountStatement + where
            if (o != null)
            {
                recordCount = (int)o;
            }
            return recordCount;
        }

        public IList<TEntity> GetPaged(string where, bool hideDeleted, string sortExpression, int maximumRows, int startRowIndex)
        {
            if (string.IsNullOrEmpty(GetPagedStatement))
            {
                throw new Exception("自定义分页的存储过程必须存在，并且要在子类中赋值!");
            }

            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            parameters.Add(new DatabaseParameter("startRowIndex", DbType.Int32, startRowIndex));
            parameters.Add(new DatabaseParameter("maximumRows", DbType.Int32, maximumRows));
            parameters.Add(new DatabaseParameter("sortExpression", DbType.String, sortExpression));

            if (hideDeleted)
            {
                where = DataAccessHelper.AppendDeletedWhereClause(where);
            }
            IEnumerable<DatabaseParameter> ps = GetSearchParameters(where, sortExpression);
            foreach (DatabaseParameter p in ps)
            {
                parameters.Add(p);
            }

            IList<TEntity> entities = Fetch(GetPagedStatement, parameters, new Action<TEntity, IDataReader>(LoadProperty), false, null, this.CommandType);
            return entities;
        }

        #endregion

        #region Insert

        public virtual bool Insert(TEntity entity, bool removeCache, string[] cacheKey)
        {
            int returnValue = 0;
            if (string.IsNullOrEmpty(InsertStatement))
            {
                throw new Exception("InsertStatement不能为空.");
            }

            returnValue = ExecuteNonQuery(entity, InsertStatement, new Action<TEntity, Database, DbCommand>(PopulateInsertParameters), null, removeCache, cacheKey, this.commandType);

            return returnValue > 0;
        }

        #endregion

        #region Update

        public virtual bool Update(TEntity entity, bool removeCache, string[] cacheKey)
        {
            int returnValue = 0;
            if (string.IsNullOrEmpty(UpdateStatement))
            {
                throw new Exception("UpdateStatement不能为空.");
            }

            returnValue = ExecuteNonQuery(entity, UpdateStatement, new Action<TEntity, Database, DbCommand>(PopulateUpdateParameters), null, removeCache, cacheKey, this.commandType);

            return returnValue > 0;
        }

        #endregion

        #region Delete

        public virtual bool Delete(Guid id, bool removeCache, string[] cacheKey)
        {
            int returnValue = 0;
            if (string.IsNullOrEmpty(DeleteByIdStatement))
            {
                throw new Exception("DeleteByIdStatement不能为空.");
            }

            returnValue = ExecuteNonQuery<Guid>(DeleteByIdStatement, new Action<Database, DbCommand, Guid>(PopulateDeleteParameters), id, null, removeCache, cacheKey, this.commandType);

            return returnValue > 0;
        }

        #endregion

        #region 辅助方法
        protected virtual IEnumerable<DatabaseParameter> GetSearchParameters(string where, string orderBy)
        {
            where = DataAccessHelper.TrimCriteria(where);
            
            List<DatabaseParameter> parameters = new List<DatabaseParameter>();
            if (where != null && where.Length > 0)
            {
                parameters.Add(new DatabaseParameter("WhereCondition", DbType.String, where));
            }
            if (!string.IsNullOrEmpty(orderBy))
            {
                parameters.Add(new DatabaseParameter("OrderBy", DbType.String, orderBy));
            }

            return parameters;
        }

        protected virtual void PopulateGuidIdParameters(Database db, DbCommand cmd, Guid id)
        {
            db.AddInParameter(cmd, "Id", DbType.Guid, id);
        }

        protected virtual void PopulateStringIdParameters(Database db, DbCommand cmd, string id)
        {
            db.AddInParameter(cmd, "Id", DbType.AnsiString, id);
        }
        
        protected virtual void PopulateInt32IdParameters(Database db, DbCommand cmd, Int32 id)
        {
            db.AddInParameter(cmd, "Id", DbType.Int32, id);
        }
        
        protected virtual void PopulateIdParameters(Database db, DbCommand cmd)
        {
            db.AddInParameter(cmd, "Id", DbType.Guid, "Id", DataRowVersion.Current);
        }
        
        protected virtual void PopulateDeleteParameters(Database db, DbCommand cmd)
        {
            //PopulateIdParameters(db, cmd);
            db.AddInParameter(cmd, "Id", DbType.Guid, "Id", DataRowVersion.Original);
        }

        #endregion
    }
}
